package com.scalar.chiptrack.wipProcessView;

import com.scalar.chiptrack.wipProcessView.NodeList;
import com.scalar.chiptrack.wipProcessView.Edge;
import com.scalar.chiptrack.wipProcessView.EdgeList;
import com.scalar.chiptrack.wipProcessView.Node;

import java.io.Serializable;


/**
 * This is a dataStructure used to represent the Process View Flow Graph.<br>
 * It consists of NodeList and EdgeList objects.<br>
 * NodeList is a collection of <Node> objects. These objects should be in <b>BFS</b> ( Breadth First Search ) Order<br>
 * EdgeList is a collection of <Edge> objects. These edges  should in <b>BFS</b> ( Breadth First Search ) Order<br>
 * User: Sujan
 * Date: May 3, 2005
 * Time: 12:59:22 PM
 */
public class ProcessViewGraph implements Serializable
{
	private com.scalar.chiptrack.wipProcessView.NodeList nodeList;
	private EdgeList edgeList;
	private int maxLevel 		= 0;
    private int nodeDistance 	= 50;
	private int verticalDistance= 40;
	private int leftGap 		= 30;
	private int topGap 			= 30;
	Node prevNodeAtLevel[] = new com.scalar.chiptrack.wipProcessView.Node[maxLevel+1];
	public ProcessViewGraph ()
	{
		nodeList = new NodeList();
		edgeList = new EdgeList ();
	}
    public void drawGraph ()
	{
		Node rootNode = ( Node )nodeList.get ( 0 );
        setLevel ( rootNode, 0, -1 );
		setXCoordinate ( rootNode, 0 ,rootNode );
		setFinalXpos();
		setYPosition ( );
		prevNodeAtLevel = new Node[ maxLevel+1 ];
		allignPosition ( rootNode );
	}
	public void setLevel ( Node root, int level, int parent )
	{
		if ( root.getLevel () < 0 )
		{
			root.setLevel ( level );
			root.setParent ( parent );
			if ( level > maxLevel )
			{
				maxLevel = level;
				prevNodeAtLevel = new Node[maxLevel+1];
			}
		}
		if ( root.getLeftChild() != null )
			setLevel ( root.getLeftChild(),level+1, root.getIndex () );
		if ( root.getRightSibling () != null )
			setLevel ( root.getRightSibling (), level, root.getParentIndex() );
	}

	public void setXCoordinate ( Node root, int xpos, Node relativeParent )
	{
		root.x = xpos;
		root.setRelativeParent ( relativeParent );
		Node prevNode = prevNodeAtLevel[root.getLevel()];
		prevNodeAtLevel[root.getLevel ()] = root;
		if ( prevNode != null )
		{
			int overlapDiff = root.getAbsX() - ( prevNode.getAbsX() + prevNode.getWidth() + nodeDistance );
			if ( overlapDiff < 0 ) //overlapping
			{
				relativeParent.absX = relativeParent.absX + ((-1) * overlapDiff );
			}
		}
		Node leftChild = root.getLeftChild();
		if ( leftChild != null )
		{
			NodeList childList = root.getChilds();
			int childCount = childList.size();
			int relDiff = 0 - (( (( childCount - 1 ) * nodeDistance) + ( childCount*root.getWidth () ) )/2 - ( root.getWidth ()/2 ) );
			setXCoordinate ( leftChild, xpos+relDiff , relativeParent );
		}
		else //need to set the absolute position in relativeParent.
		{
			if ( relativeParent != null )
			{
				if ( relativeParent.absX < 0 )
				{
					relativeParent.absX = -xpos + leftGap;
				}
			}
		}
		if ( root.getRightSibling () != null )
		{
			int absX = -1;
			if ( relativeParent.absX >= 0 )
			{
				absX = relativeParent.absX + xpos+root.getWidth() + nodeDistance;
			}
			else
			{
				absX = relativeParent.x + xpos+root.getWidth() + nodeDistance;
			}
			root.getRightSibling().absX = absX;
			setXCoordinate ( root.getRightSibling(), 0 , root.getRightSibling() );
		}
	}
	public void setFinalXpos ( )
	{
		int nodeListSize = nodeList.size();
		for ( int i=0; i < nodeListSize; i++ )
		{
			Node node =  ( Node )nodeList.get ( i );
			if ( node.absX < 0 )
			{
				Node relativeParent = node.getRelativeParent ();
                if ( relativeParent != null )
                {
                    node.absX = node.x + relativeParent.absX;
                }
            }
		}
	}

	public void setYPosition ( )
	{
		int nodeListSize = nodeList.size();
		Node node = null;
		for ( int i=0; i < nodeListSize; i++ )
		{
			node = ( Node )nodeList.get ( i );
			node.y = node.getLevel () * ( verticalDistance + node.getHeight ()  ) + topGap;
		}
	}

    public void allignPosition ( Node root ) //BFS search
	{
		//System.out.println ( "-->" + root );
		Node prevNode = prevNodeAtLevel[root.getLevel()];
		prevNodeAtLevel[root.getLevel ()] = root;
		if ( root.getLeftSibling() == null && root.getRightSibling () == null  )
		{
			NodeList parents = getParents( root );
			int parentCount = parents.size();

			if ( parentCount > 1 )
			{
				Node firstParent 	= parents.getNodeAt ( 0 );
				Node lastParent 	= parents.getNodeAt ( parentCount-1 );
				int tempAbsX = firstParent.getAbsX() + ( ( lastParent.getAbsX() + lastParent.getWidth () - firstParent.getAbsX () )/2 - root.getWidth ()/2 );
				root.absX = tempAbsX;
				if ( prevNode != null )
				{
					//int overlapDiff = root.getAbsX() - ( prevNode.getAbsX() + prevNode.getWidth() + nodeDistance );
					int overlapDiff = prevNode.getAbsX() - ( root.getAbsX() + root.getWidth() + nodeDistance ); 
					if ( overlapDiff < 0 ) //overlapping
					{
						root.absX = root.getAbsX() + ( overlapDiff );
					}
				}
			}
			else if ( parentCount == 1 )
			{
				root.absX = parents.getNodeAt ( 0 ).getAbsX();
			}
		}
		if ( root.getRightSibling() != null )
		{
			allignPosition ( root.getRightSibling() );
		}
		if ( root.getLeftChild() != null )
		{
			allignPosition ( root.getLeftChild() );
		}
	}

	public void displayNodes()
	{
		System.out.println ( "-------------NODES:" + nodeList.size() +"-------------" );
		for ( int i = 0; i < nodeList.size() ; i++ )
		{
			System.out.println ( nodeList.getNodeAt ( i ).getId() );
		}
	}

	public void displayEdges()
	{
		System.out.println ( "-------------EDGES:" + edgeList.size() +"-------------" );
		for ( int i = 0; i < edgeList.size() ; i++ )
		{
			Edge edge = ( Edge ) edgeList.get ( i );
			Node from = nodeList.getNodeAt ( edge.getFrom () );
			Node to = nodeList.getNodeAt ( edge.getTo () );
			System.out.println ( from.getId() + "---->" + to.getId() );
		}
	}
	public NodeList getNodeList ()
	{
		return nodeList;
	}

	public void setNodeList ( NodeList nodeList )
	{
		this.nodeList = nodeList;
	}

	public EdgeList getEdgeList ()
	{
		return edgeList;
	}

	public void setEdgeList ( EdgeList edgeList )
	{
		this.edgeList = edgeList;
	}

	public Node getNodeAt ( int idx )
	{
		Node node = null;
		if ( nodeList != null )
		{
			try
			{
				node = ( Node )nodeList.get ( idx );
			}
			catch ( Exception e )
			{
				e.printStackTrace();
			}
		}
		return node;
	}
	public Node createNode ( int id )
	{
		Node node = new Node ( id, nodeList, edgeList);
		Node newNode = nodeList.addNode ( node );
		return newNode;
	}
	public void createEdge ( int fromId, int toId )
	{
		int fromIdx = nodeList.getNodeIndexWithId ( fromId );
		int toIdx 	= nodeList.getNodeIndexWithId ( toId );
		Edge edge = new Edge ( fromIdx, toIdx, nodeList, edgeList);
		edgeList.addEdge ( edge );
	}
	public void createEdge ( Node from, Node to )
	{
		int fromIdx = nodeList.getNodeIndexWithId ( from.getId () );
		int toIdx 	= nodeList.getNodeIndexWithId ( to.getId () );
		Edge edge = new Edge ( fromIdx, toIdx, nodeList, edgeList);
		edgeList.addEdge ( edge );
	}
	public NodeList getParents ( Node node )
	{
		NodeList parents = new NodeList();
		Node parentNode = null;
		Node tempNode = null;
		Edge edge = null;
		int edgeListSize = edgeList.size();
		for ( int i = 0; i < edgeListSize; i++ )
		{
			edge = ( Edge ) edgeList.get ( i );
			tempNode = nodeList.getNodeAt ( edge.getTo() );
			if ( node.getId () == tempNode.getId () )
			{
				parentNode =  nodeList.getNodeAt ( edge.getFrom () );
				if ( ( parentNode.getLevel () + 1 ) == node.getLevel () )
				{
					parents.addNode ( parentNode );
				}
			}
		}
		return parents;
	}

	public boolean isImmediateNeighbours ( Node fromNode, Node toNode )
	{
		boolean isNeighbours = false;
		NodeList levelNodes = getNodesAtLevel ( fromNode.getLevel () );
		int fromNodePosition = levelNodes.getNodeIndexWithId ( fromNode.getId () );
		int toNodePosition 	= levelNodes.getNodeIndexWithId ( toNode.getId () );
		if ( Math.abs ( fromNodePosition-toNodePosition ) == 1 )
		{
			isNeighbours = true;
		}
		return isNeighbours;
	}
	public NodeList getNodesAtLevel ( int level )
	{
		return nodeList.getNodesAtLevel ( level );
	}

	public int getMaxLevel ()
	{
		return maxLevel;
	}

	public void setMaxLevel ( int maxLevel )
	{
		this.maxLevel = maxLevel;
	}

	public int getNodeDistance ()
	{
		return nodeDistance;
	}

	public void setNodeDistance ( int nodeDistance )
	{
		this.nodeDistance = nodeDistance;
	}

	public int getVerticalDistance ()
	{
		return verticalDistance;
	}

	public void setVerticalDistance ( int verticalDistance )
	{
		this.verticalDistance = verticalDistance;
	}

	public static void main ( String [] arg )
	{
		ProcessViewGraph processViewGraph = new ProcessViewGraph ( );
		processViewGraph.drawGraph ();
	}
}
